#!/usr/bin/python
#-*- coding: utf-8 -*-
import os
import re
import json
import subprocess
import tempfile
import pynotify
import time
import gtk
import gobject

CONF_FILE = os.path.expanduser('~/.twitter-notification')
LAST_READ_FILE = os.path.expanduser('~/.twitter-notification-last-read')

APP_NAME = "Twitter Notification"
conf = {}
queue = []
last_retrieved_status_id = 0
last_read_status_id = 0
paused = False
first_run = False

def retrieve(first=False):
    global queue, last_retrieved_status_id
    
    if paused or len(queue) > 5: return
    
    print 'Retrieving...',
    
    count = 15 if first_run else 200
    url = 'http://api.twitter.com/1/statuses/home_timeline.json?count=%d' % count

    since_id = max(last_retrieved_status_id, last_read_status_id)
    if since_id:
        url += '&since_id=%d' % since_id
    
    cmd = ['curl', '--silent', '--show-error', '--user', '%(username)s:%(password)s' % conf]
    if conf['proxy_host']: cmd += ['--socks5-hostname', '%(proxy_host)s:%(proxy_port)d' % conf]
    cmd += [url]

    with tempfile.TemporaryFile() as f:
        ret_code = subprocess.call(cmd, stdout=f, stderr=2)
        if ret_code == 0:
            f.seek(0)
            result = f.read()
            if result:
                status = json.loads(result)
                if status:
                    if not isinstance(status, (list, tuple)) or 'id' not in status[0]:
                        print 'Error:', status
                        exit(-1)
                    else:
                        last_retrieved_status_id = status[0]['id']
                        print len(status)
                        queue = status + queue
                    update_status_icon_tooltip()
                    if first: show()
                else:
                    print 0
            
def show():
    global last_read_status_id
    
    if queue and not paused:
        status = queue.pop(-1)
        update_status_icon_tooltip()
        
        text = status['text']
        text = re.sub(r'http(s?)://\S+', lambda m: '<a href="{0}">{0}</a>'.format(m.group(0)), text)
        text = re.sub(r'@(\w+)', lambda m: '<a href="https://twitter.com/{0}/">@{0}</a>'.format(m.group(1)), text)
        text = '<a href="https://twitter.com/{0}/"><b>{0}</b></a> {1}'.format(status['user']['screen_name'], text)
        
        if not text.strip():
            print 'empty text after format', status['text']
        
        profile_image = '/tmp/twitter-user-%d.jpg' % status['user']['id']
        if not os.path.exists(profile_image) and subprocess.call(['curl', '--silent', '--show-error', '-o', profile_image, status['user']['profile_image_url']], stderr=2) != 0:
            profile_image = ''
        
        title = "Tweets (%d)" % len(queue) if queue else "Tweet"
        global notification # required for notification.add_action()
        notification = pynotify.Notification(title, text, profile_image)
        notification.set_category('im.received')
        notification.set_urgency(pynotify.URGENCY_NORMAL)
        notification.set_timeout(conf['notification_timeout'] * 1000)
        if queue:
            notification.add_action("mark_all_as_read","Mark all as read", mark_all_as_read)
        notification.show()
 
        last_read_status_id = status['id']
        save_last_read()

def mark_all_as_read(*args, **kwargs):
    if queue:
        global last_read_status_id
        last_read_status_id = queue[0]['id']
        save_last_read()
        del queue[:]

def restore_last_read():
    global last_read_status_id, first_run
    if os.path.exists(LAST_READ_FILE):
        with open(LAST_READ_FILE, 'r') as f:
            last_read_status_id = int(f.read().strip())
        print 'Last Read:', last_read_status_id
    else:
        print 'First Run!'
        first_run = True
        
def save_last_read():
    with open(LAST_READ_FILE, 'w') as f:
        f.write(str(last_read_status_id))

def read_conf():
    c = {}
    with open(CONF_FILE, 'r') as f:
        for line in f.readlines():
            line = line.strip()
            if not line or line.startswith('#'): continue
            name, value = line.split('=', 1)
            c[name.strip()] = value.strip()
    
    global conf
    conf['username'] = c.get('username', None)
    conf['password'] = c.get('password', None)
    conf['proxy_host'] = c.get('proxy_host', None)
    conf['proxy_port'] = int(c.get('proxy_port', 1080))
    conf['notification_timeout'] = int(c.get('notification_timeout', 30))
    conf['notification_interval'] = int(c.get('notification_interval', 50))
        
    if not (conf['username'] and conf['password']):
        print 'Twitter username/password not provided!'
        exit(-1)

def init_status_icon():
    global status_icon
    status_icon = gtk.StatusIcon()
    status_icon.set_from_stock(gtk.STOCK_MEDIA_PLAY)
    status_icon.set_tooltip(APP_NAME)
    
    menu = gtk.Menu()
    
    mi_pause = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PAUSE)
    mi_pause.connect('activate', pause, status_icon)
    menu.append(mi_pause)
    
    mi_play = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PLAY)
    mi_play.connect('activate', play, status_icon)
    menu.append(mi_play)
    
    menu.append(gtk.SeparatorMenuItem())
    
    mi_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
    mi_quit.connect('activate', quit, status_icon)
    menu.append(mi_quit)

    status_icon.connect('popup-menu', popup_menu, menu)
    status_icon.set_visible(True)

def update_status_icon_tooltip():
    status_icon.set_tooltip("%s (%d unread)." % (APP_NAME, len(queue)))

def play(widget, data=None):
    global paused
    print 'Playing...'
    paused = False
    status_icon.set_from_stock(gtk.STOCK_MEDIA_PLAY)

def pause(widget, data=None):
    global paused
    print 'Paused.'
    paused = True
    status_icon.set_from_stock(gtk.STOCK_MEDIA_PAUSE)

def quit(widget, data=None):
    gtk.main_quit()

def popup_menu(status_icon, button, activate_time, menu=None):
    menu.show_all()
    menu.popup(None, None, gtk.status_icon_position_menu, 3, activate_time, status_icon)

def timeout_callback_wrapper(func, once=False, **kwargs):
    def callback():
        func(**kwargs)
        return not once
    return callback

if __name__ == '__main__':
    pynotify.init(APP_NAME)
    read_conf()
    restore_last_read()
    init_status_icon()

    gobject.timeout_add(1000, timeout_callback_wrapper(retrieve, once=True, first=True))
    gobject.timeout_add(60 * 1000, timeout_callback_wrapper(retrieve))

    gobject.timeout_add(conf['notification_interval'] * 1000, timeout_callback_wrapper(show))

    gtk.main()

